package com.meta.nd.annotation.utils;


import com.meta.cache.NdCacheService;
import com.meta.common.ConstDef;
import com.meta.nd.annotation.bo.NdDef;
import com.meta.nd.annotation.bo.FdDef;
import com.meta.nd.util.NodeUtil;
import com.meta.nd.util.SpringBeanUtil;
import com.meta.nd.vo.Nd;
import com.meta.nd.vo.Nv;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class NdAnnotationUtil {

	private static final Logger logger = LoggerFactory.getLogger(NdAnnotationUtil.class);
	public static <T> T nv2Object(Nv nv, Class<T> clazz){
		return nv2Object(nv,clazz,null);
	}

	private static<T> T createObject( Class<T> clazz ) {
		T obj = null;
		try {
			obj = clazz.newInstance();
		} catch (InstantiationException e1) {
			String msg = String.format("无法创建对象:%s", clazz.getName());
			throw new RuntimeException();
		} catch (IllegalAccessException e1) {
			String msg = String.format("无法创建对象:%s", clazz.getName());
			throw new RuntimeException();
		}
		return obj;
	}

	private static Object getObjectValue( Object obj, String name ) {

		try {
			Field f = obj.getClass().getDeclaredField(name);
			try {
				f.setAccessible(true);
				return f.get(obj);
			} catch (IllegalArgumentException e) {
				String msg = String.format("不合法的参数:%s ",  e.getMessage());
				logger.error(msg);
			} catch (IllegalAccessException e) {
				String msg = String.format("不合法的访问:%s ", e.getMessage());
				logger.error(msg);
			}
		} catch (NoSuchFieldException e) {
			String msg = String.format("不存在的方法异常:%s ", e.getMessage());
			logger.error(msg);


		} catch (SecurityException e) {
			String msg = String.format("Security异常:%s ", e.getMessage());
			logger.error(msg);
		}
		return null;

	}
	private static void setObjectValue(  Object obj, String name, Object value) {
		try {
			Field f = obj.getClass().getDeclaredField(name);
			try {
				f.setAccessible(true);
				f.set(obj, value);
			} catch (IllegalArgumentException e) {
				String msg = String.format("不合法的参数:%s ",e.getMessage());
				logger.error(msg);
			} catch (IllegalAccessException e) {
				String msg = String.format("不合法的访问:%s ", e.getMessage());
				logger.error(msg);
			}
		} catch (NoSuchFieldException e) {
			String msg = String.format("不存在的方法异常:%s ",  e.getMessage());
			logger.error(msg);
		} catch (SecurityException e) {
			String msg = String.format("Security异常:%s ", e.getMessage());
			logger.error(msg);
		}
	}
	private static Class getClassByClassName( String className ) {
		try {
			return Class.forName(className);
		} catch (ClassNotFoundException e) {
			String msg = String.format("找不到相应类：%s",  className,e.getMessage());
			throw new RuntimeException(msg);
		}
	}


	public static<T> T nv2Object(Nv nv, Class<T> clazz, Map<Nv,Object> nvObjectMap){
		Nd nd = nv.getNd();
		T obj = createObject(clazz);
		for ( Field f : clazz.getDeclaredFields()) {
			String fdName = f.getName();
			FdDef fdAnnotation = f.getDeclaredAnnotation(FdDef.class);
			if ( fdAnnotation == null )
				continue;
			String name = fdAnnotation.value();
			//如果是固定属性
			if (name!= null && !name.equals("") && name.equals(ConstDef.FD_NAME_ID))
				setObjectValue(obj,fdName,nv.getId());
			else if (name!= null && !name.equals("") && name.equals(ConstDef.FD_NAME_PARENT_ID))
				setObjectValue(obj,fdName,nv.getParentId());
				//如果是动态定义属性
			else if ( name != null && !name.equals(""))
				setObjectValue(obj,fdName,nv.getValue(name));
				//如果是下一个Dna
			else if ( name == null || name.equals("") ) {
				String childNdName = fdAnnotation.ndName();
				if ( childNdName == null || childNdName.equals("")) continue;
				Nd childNd = nd.getChildNd(childNdName);
				if ( childNd == null ) {
					if ( nd.isCursive() && nd.getNdName().equals(childNdName))//支持节点递归
						childNd = nd;
					else continue;
				}
				if ( f.getType().isAssignableFrom(List.class ) ){
					List<Nv> childNvs = nv.getChildNvs(childNd.getNdName());
					ParameterizedType pt=(ParameterizedType)f.getGenericType();
					String typeName = pt.getActualTypeArguments()[0].getTypeName();
					Class childClazz = getClassByClassName(typeName);
					List children = nvs2Objects(childNvs, childClazz,nvObjectMap);
					if ( children != null) setObjectValue(obj,fdName,children);
				}
				else if(f.getType().isAssignableFrom(Map.class)){
					String keyFdName=fdAnnotation.keyFdName();
					String valueFdName =fdAnnotation.valueFdName();
					List<Nv> childNvs = nv.getChildNvs(childNd.getNdName());
					if (childNvs == null || childNvs.size() == 0)
						continue;
					ParameterizedType pt=(ParameterizedType)f.getGenericType();
					String typeName1 = pt.getActualTypeArguments()[0].getTypeName();
					String typeName2 = pt.getActualTypeArguments()[1].getTypeName();
					Class childClazz1 = getClassByClassName(typeName1);
					Class childClazz2 = getClassByClassName(typeName2);
					Map map = nvs2Map(childNvs,keyFdName,valueFdName,childClazz1,childClazz2);
					if ( map != null )setObjectValue(obj,fdName,map);
				}
				else if ( !f.getType().isArray()){
					List<Nv> childNvs = nv.getChildNvs(childNd.getNdName());
					List children = nvs2Objects(childNvs, f.getType(),nvObjectMap);
					if (children.size()>0)
						setObjectValue(obj,fdName,children.get(0));
				}
				else {
					String msg = String.format("不支持的属性类型：%s",  f.getType().getName());
					throw new RuntimeException(msg);
				}
			}
		}
		return obj;
	}


	public static<T1,T2> Map<T1,T2> nvs2Map(List<Nv> nvs, String keyFdName, String valueFdName, Class<T1> clazz1, Class<T2>  clazz2){
		Map<T1,T2> map = new HashMap<T1,T2>();
		for ( Nv nv : nvs ){
			Object key = nv.getValue(keyFdName);
			Object value = nv.getValue(valueFdName);
			map.put((T1) key,(T2) value);
		}
		return map;
	}

	public static<T>  List<T> nvs2Objects(List<Nv> nvs, Class<T> clazz, Map<Nv,Object> nvObjectMap ) {
		ArrayList<T> results = new ArrayList<T>();
		for ( Nv nv : nvs ) {
			T obj = nv2Object(nv,clazz,nvObjectMap);
			results.add(obj);
			if ( nvObjectMap!=null )
				nvObjectMap.put(nv,obj);
		}
		return results;
	}

	public static <T> List<Nv> nv2Object(T obj, Nd parentNd){
		List<T> list = new ArrayList<T>();
		list.add(obj);
		return objects2Nvs(list, parentNd);
	}


	public static<T1,T2> List<Nv> map2Nvs(Map<T1,T2> map, String keyFdName, Nd nd){
		List<Object> objs = new ArrayList<>();
		List<T1> keys = new ArrayList<>();
		for (T1 key : map.keySet() ){
			keys.add(key);
			T2 value = map.get(key);
			objs.add(value);
		}
		List<Nv> nvs = objects2Nvs(objs,nd.getParent());
		for ( int i =0; i<keys.size(); i++){
			Nv nv = nvs.get(i);
			nv.setFvByName(keyFdName,keys.get(i));
		}
		return nvs;
	}


	public static<T1,T2> List<Nv> map2Nvs(Map<T1,T2> map, String keyFdName, Nd nd, Map<Object,Nv> objectNvMap){
		List<Object> objs = new ArrayList<>();
		List<T1> keys = new ArrayList<>();
		for (T1 key : map.keySet() ){
			keys.add(key);
			T2 value = map.get(key);
			objs.add(value);
		}
		List<Nv> nvs = objects2Nvs(objs,nd.getParent(),objectNvMap);
		for ( int i =0; i<keys.size(); i++){
			Nv nv = nvs.get(i);
			nv.setFvByName(keyFdName,keys.get(i));
		}
		return nvs;
	}

	public static void setNvFv(Object obj, Field f, FdDef fdAnnotation, Nv nv) {

		String fdName = f.getName();
		String name = fdAnnotation.value();
		if (name!= null && !name.equals("") && name.equals(ConstDef.FD_NAME_ID)) {
			Long id = (Long)getObjectValue(obj,fdName);
			if ( id == null )nv.setId((0));
			else nv.setId(id);
			if ( nv.isIdNull())
				nv.setOperationFlag(ConstDef.OPERATION_FLAG_NEW);
			else
				nv.setOperationFlag(ConstDef.OPERATION_FLAG_UPDATE);
		}
		else if ( name != null && !name.equals("")) {
			if ( nv.getFvByName(name) != null ) {
				Object value = getObjectValue(obj,fdName);
				nv.getFvByName(name).setValue(value);
			}
		}
	}


	public static <T> List<Nv> objects2Nvs(List<T> objs, Nd parentNd, Map<Object,Nv> objNvMap){

		if ( objs ==null || objs.size() == 0 ) return null;
		List<Nv> nvs = new ArrayList<>();
		for ( T obj : objs) {
			if ( obj == null ) continue;
			Class clazz = obj.getClass();
			NdDef annotation = getNdAnnotation(clazz);
			String ndCode = annotation.value();
			String ndName = annotation.ndName();
			Nd nd = null;
			if ( parentNd == null ) {
				NdCacheService ndCacheService = SpringBeanUtil.getNdCacheService();
				nd = ndCacheService.getNd(ndCode);
			}
			else nd = parentNd.getChildNd(ndName);

			Nv nv = NodeUtil.nd2Nv(nd);
			nvs.add(nv);
			objNvMap.put(obj,nv);
			for ( Field f : clazz.getDeclaredFields()  ) {
				FdDef fdAnnotation = (FdDef)f.getDeclaredAnnotation(FdDef.class);
				if ( fdAnnotation == null ) continue;
				String fdName = f.getName();
				String name = fdAnnotation.value();
				if (name!= null && !name.equals("") )
					setNvFv(obj,f,fdAnnotation,nv);
					//如果下一个Nd
				else if( name == null || name.equals("") ) {
					String childNdName = fdAnnotation.ndName();
					if ( childNdName == null || childNdName.equals(""))
						continue;
					Nd childNd = nd.getChildNd(childNdName);
					if ( childNd == null && nd.isCursive() && nd.getNdName().equals(childNdName))
						childNd = nd;
					else if ( childNd == null) {
						String msg = String.format("找不到注解对应ndName:%s", childNdName);
						throw new RuntimeException(msg);
					}
					if ( f.getType().isAssignableFrom(List.class ) ){
						List<Nv> childNvs = objects2Nvs((List)getObjectValue(obj,fdName),nd,objNvMap);
						if ( childNvs != null)
							nv.addChildNvs(childNd.getNdName(), childNvs);
					}
					else if(f.getType().isAssignableFrom(Map.class)){
						String keyFdName=fdAnnotation.keyFdName();
						List<Nv> childNvs = map2Nvs((Map)getObjectValue(obj,fdName),keyFdName,childNd,objNvMap);
						if ( childNvs != null)
							nv.addChildNvs(childNd.getNdName(), childNvs);
					}
					else if ( !f.getType().isArray()){
						List list = new ArrayList<>();
						list.add(getObjectValue(obj,fdName));
						List<Nv> childNvs = objects2Nvs(list,nd,objNvMap);
						if ( childNvs != null)
							nv.addChildNvs(childNd.getNdName(), childNvs);
					}
					else {
						String msg = String.format("不支持的数据类型:%s", childNdName);
						throw new RuntimeException(msg);
					}
				}
			}
		}
		return nvs;
	}

	public static <T> Nv object2Nv( T obj ){
		List<T> objs = new ArrayList<T>();
		objs.add(obj);
		List<Nv> nvs = objects2Nvs(objs,null);
		return nvs.get(0);
	}


	public static <T> List<Nv> objects2Nvs(List<T> objs, Nd parentNd ) {
		if ( objs ==null || objs.size() == 0 ) return null;
		List<Nv> nvs = new ArrayList<>();
		for ( T obj : objs) {
			if ( obj == null ) continue;
			Class clazz = obj.getClass();
			NdDef annotation = getNdAnnotation(clazz);
			String ndCode = annotation.value();
			String ndName = annotation.ndName();
			Nd nd = null;
			if ( parentNd == null ) {
				if(StringUtils.isEmpty(ndCode))
					throw new RuntimeException("annotation上没有配置ndCode!");
				NdCacheService ndCacheService = SpringBeanUtil.getNdCacheService();
				nd = ndCacheService.getNd(ndCode);
			}
			else {
				if (!StringUtils.isEmpty(ndName) )
					nd = parentNd.getChildNd(ndName);
				else if (!StringUtils.isEmpty(ndCode))
					nd = parentNd.getChildNd(ndCode);
				else
					throw new RuntimeException("annotation上没有配置ndName或者ndCode!");
			}
			Nv nv = NodeUtil.nd2Nv(nd);
			nvs.add(nv);
			for ( Field f : clazz.getDeclaredFields()  ) {
				FdDef fdAnnotation = (FdDef)f.getDeclaredAnnotation(FdDef.class);
				if ( fdAnnotation == null ) continue;
				String fdName = f.getName();
				String name = fdAnnotation.value();
				if (name!= null && !name.equals("") )
					setNvFv(obj,f,fdAnnotation,nv);
					//如果下一个nd
				else if( name == null || name.equals("") ) {
					String childNdName = fdAnnotation.ndName();
					if ( childNdName == null || childNdName.equals(""))
						continue;
					Nd childNd = nd.getChildNd(childNdName);
					if ( childNd == null && nd.isCursive() && nd.getNdName().equals(childNdName))
						childNd = nd;
					else if ( childNd == null) {
						String msg = String.format("找不到注解对应ndName:%s", childNdName);
						throw new RuntimeException(msg);
					}
					if ( f.getType().isAssignableFrom(List.class ) ){
						List<Nv> childNvs = objects2Nvs((List)getObjectValue(obj,fdName),nd);
						if ( childNvs != null)
							nv.addChildNvs(childNd.getNdName(), childNvs);
					}
					else if(f.getType().isAssignableFrom(Map.class)){
						String keyFdName=fdAnnotation.keyFdName();
						List<Nv> childNvs = map2Nvs((Map)getObjectValue(obj,fdName),keyFdName,childNd);
						if ( childNvs != null)
							nv.addChildNvs(childNd.getNdName(), childNvs);
					}
					else if ( !f.getType().isArray()){
						List list = new ArrayList<>();
						list.add(getObjectValue(obj,fdName));
						List<Nv> childNvs = objects2Nvs(list,nd);
						if ( childNvs != null)
							nv.addChildNvs(childNd.getNdName(), childNvs);
					}
					else {
						String msg = String.format("不支持的数据类型:%s", childNdName);
						throw new RuntimeException(msg);
					}
				}
			}
		}
		return nvs;
	}

	public static<T> NdDef getNdAnnotation(Class<T> clazz ) {

		NdDef annotation = (NdDef) clazz.getAnnotation(NdDef.class);
		if ( annotation == null ) {
			String msg = String.format("%s的注解NdDef没有定义",clazz.getName());
			throw new RuntimeException(msg);
		}
		String  ndCode = annotation.value();
		String ndName = annotation.ndName();
		if ((ndCode == null || ndCode.equals("")) && (ndName == null || ndName.equals(""))) {
			String msg = String.format("%s的注解NdDef没有定义", clazz.getName());
			throw new RuntimeException(msg);
		}
		return  annotation;
	}
}
